/**
 * The MIT License
 *
 * Copyright (c) 2010 Benjamin Klum
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.helgoboss.scala_xml_tree_builder

abstract class XmlTree {
    private var processor: XmlTreeProcessor = _
    private var pendingTag: Option[Tag] = None

    /**
     * Convert symbols to tags
     */
    protected implicit def symbolToFreshTag(symbol: Symbol) = {
       new FreshTag(symbol.name)
    }
   
    /**
     * Convert strings to tags
     */
    protected implicit def stringToFreshTag(string: String) = {
       new FreshTag(string)
    }
      
    /**
     * Explicitly create tags using tag("...")
     */
    def tag(name: String) = {
       new FreshTag(name)
    }
    
    /**
     * Explicitly create text content using text("...") within blocks
     */
    def text(content: String) {
        processor.text(content)
    }
   
    private def finishPendingTag {
       pendingTag.foreach(_.finish)
    }
       
    protected trait Tag {
        def finish
    }
    
    /**
     * A tag in phase one. Neither attributes have been defined yet nor content.
     */
    protected class FreshTag(name: String) extends Tag {
        /* There might be a pending tag before the definition of this tag. Finish it. */
        finishPendingTag
        
        /* At this time, the tag has not been written yet because its definition still might be extended. So we call it pending. */
        pendingTag = Some(this)
    
        /**
         * Allow to immediately create a sub tree without defining attributes or injecting text. This finishes this tag.
         */
        def apply (body: => Any) {
            pendingTag = None
            startTag(name, Nil)
            body
            endTag(name)
        }

        /**
         * Allow to apply a list of attributes first and continue defining the tag after that. The tag becomes a
         * TagWithAttributes and thereby goes over into phase two.
         */
        def apply (attributes: (String, String)*) = {
            new TagWithAttributes(name, attributes.toList)
        }

        /**
         * Allow to immediately inject text without defining attributes or creating a sub tree. This finishes this tag.
         */
        def <<(content: String) {
            pendingTag = None
            startTag(name, Nil)
            text(content)
            endTag(name)
        }
        
        /**
         * Writes this tag without any attributes and content
         */
        def finish {
            pendingTag = None
            startEndTag(name, Nil)
        }
    }
   
    /**
     * A tag in phase two. Attributes have been defined already but no content.
     */
    protected class TagWithAttributes(name: String, attributes: List[(String, String)]) extends Tag {
        /* At this time, the tag still has not been written yet because its definition still might be extended. So we call it pending. */
        pendingTag = Some(this)
        
        /**
         * Allow to create a sub tree right after having defined the attributes. This finishes this tag.
         */
        def apply(body: => Unit) {
            pendingTag = None
            startTag(name, attributes)
            body
            endTag(name)
        }
        
        /**
         * Allow to inject text right after having defined the attributes. This finishes this tag.
         */
        def <<(content: String) {
            pendingTag = None
            startTag(name, attributes)
            text(content)
            endTag(name)
        }
        
        /**
         * Writes this tag with attributes but without any content
         */
        def finish {
            pendingTag = None
            startEndTag(name, attributes)
        }
    }

    def structure
   
    def processWith(processor: XmlTreeProcessor) {
       this.processor = processor
       structure
    }
    
    private def startTag(name: String, attributes: List[(String, String)]) {
        processor.startTag(name, attributes)
    }
    
    private def endTag(name: String) {
        /* There might be a pending tag right before the end of a block. Finish it. */
        finishPendingTag
        processor.endTag(name)
    }
    
    private def startEndTag(name: String, attributes: List[(String, String)]) {
        processor.startEndTag(name, attributes)
    }
}
